package com.sali.java_stream;

import com.sali.pojo.model.*;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.*;
import java.util.function.DoubleSupplier;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@SpringBootTest
class StreamApplication {

    List<User> userList1 = new ArrayList<>();
    List<User> userList2 = new ArrayList<>();

    List<ShopCart> shopCarts = new ArrayList<>();

    List<Book> bookList = new ArrayList<>();

    /**
     * @description: 数据准备
     * @author: sali
     * @date: 2025/1/31
     */ {
        userList1.add(new User(1, "小A", "123456", 16, 1, 20000d));
        userList1.add(new User(2, "小B", "123456", 22, 1, 100000d));
        userList1.add(new User(3, "小C", "123456", 13, 1, 321313d));
        userList1.add(new User(4, "小D", "123456", 26, 1, 780000d));
        userList1.add(new User(5, "小E", "123456", 32, 1, 580000d));
        userList1.add(new User(5, "小E", "123456", 32, 1, 580000d));

        userList2.add(new User(6, "小F", "112358", 19, 0, 2323212d));
        userList2.add(new User(2, "小B", "123456", 22, 1, 100000d));

        shopCarts.add(new ShopCart(1, "手机1", 1d, 1));
        shopCarts.add(new ShopCart(2, "手机2", 2d, 2));
        shopCarts.add(new ShopCart(3, "手机3", 3d, 3));

        bookList = Lists.newArrayList(
                new Book("BookA", "Java", 128),
                new Book("BookB", "Java", 88),
                new Book("BookC", "Java", 118),
                new Book("BookD", "Python", 98)
        );
    }


    /**
     * @description: 生成stream
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void getStream() {
        Stream<User> stream1 = userList1.stream();
        Stream<User> stream2 = userList1.parallelStream();
        Integer[] nums = new Integer[10];
        Stream<Integer> integers = Arrays.stream(nums);
        Stream<List<User>> userList3 = Stream.of(userList1, userList2);
    }


    /**
     * @description: 多个集合合并，而后分组
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void groupByUsage() {
        List<Apple> appleRedList = Lists.newArrayList(
                Apple.builder().id(1).color("red").weight(10).build(),
                Apple.builder().id(2).color("red").weight(11).build());

        List<Apple> appleGreenList = Lists.newArrayList(
                Apple.builder().id(3).color("green").weight(5).build(),
                Apple.builder().id(4).color("green").weight(6).build());
        List<Apple> appleList = Stream.of(appleRedList, appleGreenList).flatMap(Collection::stream).collect(Collectors.toList());

        Map<String, List<Apple>> collect = appleList.stream().collect(Collectors.groupingBy(Apple::getColor));
        System.out.println(collect);
    }


    /**
     * @description: map集合 转 list集合
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void mapToList() {
        Map<String, List<Apple>> appleMap = new HashMap<>();

        List<Apple> appleRedList = Lists.newArrayList(
                Apple.builder().id(1).name("红苹果一号").color("red").weight(10).build(),
                Apple.builder().id(2).name("红苹果二号").color("red").weight(11).build());
        appleMap.put("red", appleRedList);

        List<Apple> appleGreenList = Lists.newArrayList(
                Apple.builder().id(3).name("绿苹果一号").color("green").weight(5).build(),
                Apple.builder().id(4).name("绿苹果二号").color("green").weight(6).build());
        appleMap.put("green", appleGreenList);

        Set<String> appleNameList = appleMap.entrySet().stream()
                .flatMap(stringListEntry -> stringListEntry.getValue().stream())
                .distinct()
                .filter(apple -> !Objects.equals(apple.getColor(), "green"))
                .map(Apple::getName)
                .collect(Collectors.toSet());

        System.out.println(appleNameList);
    }

    /**
     * @description: peek实现数据属性更改
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void PeekUsage() {
        List<User> collect1 = userList1.stream().peek(user -> user.setPassword(null)).collect(Collectors.toList());
        collect1.forEach(System.out::println);
    }

    /**
     * @description: filter、sorted、limit基础用法
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void baseUsage() {
        List<User> collect = userList1.stream().filter(user -> user.getAge() > 16).sorted(Comparator.comparingInt(User::getAge)).limit(2)
//                .findFirst()
                .collect(Collectors.toList());
        collect.forEach(System.out::println);

        System.out.println("================================");

        List<User> collect2 = userList1.stream().filter(u -> u.getAge() > 16).peek(u -> u.setAge(u.getAge() + 1)).collect(Collectors.toList());
        collect2.stream().forEach(System.out::println);
    }

    /**
     * @description: map进行元素转换
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void mapUsage() {
        List<String> collect3 = userList1.stream().map(u -> u.getUsername()).collect(Collectors.toList());
        collect3.stream().forEach(System.out::println);

        System.out.println("================================");

        List<Map<String, Object>> collect4 = userList1.stream().map(u -> {
            Map<String, Object> map = new HashMap<>();
            map.put("id", u.getId());
            map.put("user", u.getUsername());
            map.put("sex", u.getSex());
            return map;
        }).collect(Collectors.toList());
        collect4.stream().forEach(System.out::println);

        System.out.println("================================");

        List<UserVo> collect5 = userList1.stream().map(u -> {
            UserVo userVo = new UserVo();
            userVo.setId(u.getId());
            userVo.setUsername(u.getUsername());
            userVo.setSex(u.getSex() == 1 ? "男" : "女");
            return userVo;
        }).collect(Collectors.toList());
        collect5.stream().forEach(System.out::println);
    }

    /**
     * @description: sort用法
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void sortUsage() {
        List<User> collect6 = userList1.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).collect(Collectors.toList());
        collect6.stream().forEach(System.out::println);
    }

    /**
     * @description: distinct去重用法
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void distinctUsage() {
        // 注意：如果是对象，这个过滤需重写equals及hash方法，也可以直接加个@Data
        List<User> collect7 = userList1.stream().distinct().collect(Collectors.toList());
        collect7.stream().forEach(System.out::println);
    }

    /**
     * @description: 利用stream实现分页器
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void myPageHelper() {
        int pageNo = 1;
        int pageSize = 10;

        int skip = (pageNo - 1) * pageSize;
        List<User> collect8 = userList1.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).skip(skip).limit(pageSize).collect(Collectors.toList());
        collect8.stream().forEach(System.out::println);
    }

    /**
     * @description: distinct、sum、max、min
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void resultUsage() {
        List<User> collect9 = userList1.stream().distinct().collect(Collectors.toList());
        long count = collect9.stream().distinct().count();
        Optional<User> first = userList1.stream().distinct().findFirst();
        Integer age = first.orElse(new User()).getAge();

        System.out.println("================================");

        int sum = userList1.stream().mapToInt(u -> u.getAge()).sum();
        System.out.println(sum);

        User user1 = userList1.stream().max(Comparator.comparingInt(User::getAge)).get();
        System.out.println(user1);
        Integer integer = userList1.stream().map(u -> u.getAge()).max(Comparator.comparingInt(age2 -> age2)).get();
        System.out.println(integer);

        System.out.println("================================");

        OptionalDouble optionalDouble = shopCarts.stream().mapToDouble(shopcart -> shopcart.getPrice()).min();
        double v = optionalDouble.orElseGet(new DoubleSupplier() {
            @Override
            public double getAsDouble() {
                // todo
                return 0;
            }
        });
        System.out.println(v);

    }

    /**
     * @description: flatmap进行集合合并、集合取交集
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void flatMapUsage() {
        // 合并
        List<User> allUserList = Stream.of(userList1, userList2).flatMap(s -> s.stream()).collect(Collectors.toList());
        allUserList.forEach(System.out::println);

        System.out.println("================");

        // 合并 + 映射
        List<String> allUserList2 = Stream.of(userList1, userList2).flatMap(s -> s.stream()).map(u -> u.getUsername()).collect(Collectors.toList());
        allUserList2.forEach(System.out::println);

        System.out.println("================");

        // 交集
        List<User> collect = userList1.stream().filter(userList2::contains).collect(Collectors.toList());
        collect.forEach(System.out::println);
    }

    /**
     * @description: map reduce获取购物车总价格
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void reduceUsage() {
        // 注意起始位
        double doubleValue = shopCarts.stream().map(shopCart -> shopCart.getPrice() * shopCart.getNum()).reduce(0d, (a, b) -> a + b).doubleValue();
        System.out.println(doubleValue);

        System.out.println("==========");

        // 或者用如下写法
        double sum = shopCarts.stream().mapToDouble(shopCart -> shopCart.getPrice() * shopCart.getNum()).sum();
        double asDouble = shopCarts.stream().mapToDouble(shopCart -> shopCart.getPrice()).average().getAsDouble();
        double asDouble1 = shopCarts.stream().mapToDouble(shopCart -> shopCart.getPrice()).max().getAsDouble();
        double asDouble2 = shopCarts.stream().mapToDouble(shopCart -> shopCart.getPrice()).min().getAsDouble();
        System.out.println(sum);
        System.out.println(asDouble);
        System.out.println(asDouble1);
        System.out.println(asDouble2);
    }

    /**
     * @description: anyMatch用法
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void anyMatchUsage() {
        // 只要集合中的某个元素满足条件就是true，否则为false
        boolean b = userList1.stream().anyMatch(new Predicate<User>() {
            @Override
            public boolean test(User user) {
                return user.getAge() > 16;
            }
        });
        System.out.println(b);
    }

    /**
     * @description: Optional判空
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void optionalUsage() {
        Integer a = null;
        System.out.println("Optional结果：" + Optional.ofNullable(Double.valueOf(a)).orElse(null));
    }

    /**
     * @description: collect实现list转map
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void collectUsage01() {


        Map<String, List<Book>> resultMap = bookList.stream()
                .collect(HashMap::new, (map, book) -> {
                    List<Book> categoryList = Optional.ofNullable(map.get(book.getCategory()))
                            .orElseGet(() -> {
                                List<Book> tmpList = new ArrayList<>();
                                map.put(book.getCategory(), tmpList);
                                return tmpList;
                            });
                    categoryList.add(book);
                }, HashMap::putAll);

        System.out.println(resultMap);
    }

    /**
     * @description: collect实现groupingBy, partitioningBy, summingInt, summarizingInt
     * @author: sali
     * @date: 2025/1/31
     */
    @Test
    void collectUsage02() {
        // 对Stream中的Book按category分类
        Map<String, List<Book>> result2 = bookList.stream().collect(Collectors.groupingBy(Book::getCategory));
        System.out.println(result2);

        // 将Stream中Book的价格大于100的分为true类，其他的分为false类
        Map<Boolean, List<Book>> result3 = bookList.stream().collect(Collectors.partitioningBy(b -> b.getPrice() > 100));
        System.out.println(result3);

        // 将Stream中所有Book的价格求和
        Integer sum = bookList.stream().collect(Collectors.summingInt(Book::getPrice));
        System.out.println("总和:" + sum);

        // 对Stream中所有Book的价格生成统计信息
        IntSummaryStatistics summary = bookList.stream().collect(Collectors.summarizingInt(Book::getPrice));
        System.out.println("最高价格:" + summary.getMax());
        System.out.println("最低价格:" + summary.getMin());
        System.out.println("平均价格:" + summary.getAverage());
        System.out.println("价格数量:" + summary.getCount());
    }

}
